/*	This software is under  GNU GENERAL PUBLIC LICENSE Version 2, June 1991
*	for more information read COPYING.TXT
*	Dimitri Sean Ribeiro - 5118549
*/

//include section
#include "comando.h"

/******************************************************************************************/

/*
* 	EN:
* 	This function checks if the command line has any modificador and do the appropriate work
*	
*	PT:
 *	Esta função verifica se o comando possui algum modificador, executando-o de acordo 
 *	com este. Executa normalmente o comando caso não haja nenhum modificador
 *	
 *	Esta função trata os seguintes modificadores:
 *	
 *	>>	redirecionamento de saída no modo append
 *	>	redirecionamento no modo overwrite
 *	2>	redirecionamento da saída de erro
 *	&>	redirecionamento de saída e saída de erros para o arquivo
 *	<	redirecionamento da entrada a partir de um arquivo
 *	|	pipe
 *	&	execuão em background
 *
 */
 
int select_command(command_line_t command) {
	int i, j;
	char *arq;
	command_line_t com_aux;

	for (i = 1; i <= command.argc; i++) {
		if (command.argv[i] != NULL){
			//exit to the file - append
			if(strcmp(command.argv[i],">>") == 0) {
 				arq = command.argv[i+1];
				free_com(&command, i);  //delete >> and the file name
				execute_command_arq(command, arq, MODOAPPEND);
				return(EXIT_SUCCESS);
			}
			//exit to the file - overwrite
			else if(strcmp(command.argv[i],">") == 0) {
				arq = command.argv[i+1];
				free_com(&command, i);  //delete > and the file name
				execute_command_arq(command, arq, MODOOVERWRITE);
				return(EXIT_SUCCESS);
			}
			//error's exit to file - overwrite
			else if(strcmp(command.argv[i],"2>") == 0) {
				arq = command.argv[i+1];
				free_com(&command, i);  //delete 2> and the file name
				execute_command_err(command, arq);
				return(EXIT_SUCCESS);
			}
			//error's exit to file - overwrite
			else if(strcmp(command.argv[i],"&>") == 0) {
				arq = command.argv[i+1];
				free_com(&command, i);  //delete &> and the file name
				execute_command_dup(command, arq);
				return(EXIT_SUCCESS);
			}
			//program input
			else if(strcmp(command.argv[i],"<") == 0) {
				arq = command.argv[i+1];
				free_com(&command, i);  //delete < and the file name
				execute_command_arq_input(command, arq);
				return(EXIT_SUCCESS);
			}
			//pipe
			else if(strcmp(command.argv[i],"|") == 0) {
				com_aux.argc =  command.argc - i - 1;
				com_aux.argv = (char **) malloc ((command.argc - i - 1) * sizeof(char *));

				//com_aux recieves the pipe command
				for (j = i + 1; j < command.argc; j++){
					com_aux.argv[j - i - 1] = command.argv[j];
				}
				free_com(&command, i);
				execute_command_pipe(command, &com_aux);
				return(EXIT_SUCCESS);
			}
			//background
			else if(strcmp(command.argv[i],"&") == 0) {
				free_com(&command, i);		//delete &
				execute_command(command, BACKGROUND);
				return(EXIT_SUCCESS);
			}
         
		}
	}
	
	//execute the command line if it has none of symbols above listed
	execute_command(command, FOREGROUND);
	
	return(EXIT_SUCCESS);
}

/******************************************************************************************/

/*
*	EN: 
*	this function executes the command line in background or foreground according to the input line.
*	
*	PT:
 *	Esta função executa o comando em background ou em foreground, de acordo com o
 *	comando de entrada.
 *
 */
 
int execute_command(command_line_t command, int tipo) {
    pid_t pid;
    int status;

    //fork the child process to execute the command
    pid = fork ();

    if (pid == -1 ) { //pid < 0 error
	    fatal();
    }
    else {
		if (pid == 0) { //pid == 0 child
	    	if(tipo == BACKGROUND) {
				add_process(command,getpid());
				raise(SIGTTOU);
			}
			
	    	signal(SIGINT,get_signal_SIGINT);
			signal(SIGTSTP,get_signal_SIGTSTP);
	    	
	    	//execute command
	    	if (execvp (command.argv[0], command.argv) == -1) {
				fprintf(stderr,"%s: command not found.\n", command.argv[0]);
				exit(EXIT_FAILURE);
	    	}
		}
		else { //father
			signal(SIGTSTP,get_signal_SIGTSTP);
			if (tipo == FOREGROUND)
				waitpid (pid, &status, 0);
		}

	}
	return(EXIT_SUCCESS);
}
/******************************************************************************************/

/*
*	EN: this function executes the command redirecting the output to a file. Append ou overwrite mode.
*
*	PT
 *	Esta função executa o comando redirecionando a saída para um arquivo. Pode ser
 *	no modo apeend ou overwrite.
 *
 */

int execute_command_arq(command_line_t command, char *arq, int modo) {
	pid_t pid;
	int status;

	//fork the child process to execute the command
	pid = fork ();

	if (pid == -1 ) { //pid < 0 error
		fatal();
	}
	else {
		if (pid == 0) { //pid == 0 child
			if(fclose(stdout)) {
				fatal();
				exit(EXIT_FAILURE);
			}

			if (modo == MODOAPPEND) {
				stdout = fopen(arq,"a");
			}
			else {
				stdout = fopen(arq,"w");
			}
			//execute command
			if (execvp(command.argv[0], command.argv) == -1) {
				fprintf(stderr,"%s: command not found.\n", command.argv[0]);
				exit(EXIT_FAILURE);
			}

			if (fclose(stdout)) {
				fatal();
				exit(EXIT_FAILURE);
			}
		}
		else { //father
			waitpid (pid, &status, 0);
		}
	}
	return(EXIT_SUCCESS);
}

/******************************************************************************************/

/*
*	EN:
*	this function executes the command redirecting the erros' output to a file
 *	
 *	PT:
*	 Esta função executa o comando redirecinando a saída de erros para um arquivo.
 *
 */

int execute_command_err(command_line_t command, char *arq) {
	pid_t pid;
	int status;

	//fork the child process to execute the command
	pid = fork ();

	if (pid == -1 ) { //pid < 0 error
		fatal();
	}
	else {
		if (pid == 0) {	//pid == 0 child
			if(fclose(stderr)) {
				fatal();
 				exit(EXIT_FAILURE);
			}
        	
			stderr = fopen(arq,"w");
           
			//execute command
			if (execvp(command.argv[0], command.argv) == -1) {
				fprintf(stderr,"%s: command not found.\n", command.argv[0]);
				exit(EXIT_FAILURE);
 			}

			if (fclose(stderr)) {
				fatal();
				exit(EXIT_FAILURE);
			}
		}
		else { //father
			waitpid (pid, &status, 0);
		}
	}
	return(EXIT_SUCCESS);
}

/******************************************************************************************/

/*
*	EN:
*	this function execute the command redirectin the standard and the errors' outputs to a file
*
*	PT:
 *	Esta função executa o comando redirecinando a saída de erros e a normal 
 *	para um arquivo.
 *
 */

int execute_command_dup(command_line_t command, char *arq) {
	pid_t pid;
	int status;

	//fork the child process to execute the command
	pid = fork ();

	if (pid == -1 ) { //pid < 0 error
		fatal();
	}
	else {
		if (pid == 0) { //pid == 0 child
			if(fclose(stdout) || fclose(stderr)) {
				fatal();
				exit(EXIT_FAILURE);
			}

			stderr = fopen(arq,"w");
			stdout = fopen(arq,"w");
           	 
			//execute command
			if (execvp(command.argv[0], command.argv) == -1) {
 				fprintf(stderr,"%s: command not found.\n", command.argv[0]);
				exit(EXIT_FAILURE);
			}

			if (fclose(stdout) || fclose(stderr)) {
 				fatal();
				exit(EXIT_FAILURE);
			}
		}
		else { //father
 			waitpid (pid, &status, 0);
		}
	}
	return(EXIT_SUCCESS);
}

/******************************************************************************************/

/*
*	EN:
*	this function executes the command redirecting the input from a file
*
*	PT:
 *	Esta função executa o comando redirecinando a entrada a partir de um arquivo.
 *
 */

int execute_command_arq_input(command_line_t command, char *arq) {
	pid_t pid;
	int status;

	//fork the child process to execute the command
	pid = fork ();

	if (pid == -1 ) { //pid < 0 error
		fatal();
	}
	else {
		if (pid == 0) { //pid == 0 child
			if(fclose(stdin)) {
				fatal();
				exit(EXIT_FAILURE);
			}

			stdin = fopen(arq,"r");

			//execute command
			if (execvp(command.argv[0], command.argv) == -1) {
				fprintf(stderr,"%s: command not found.\n", command.argv[0]);
				exit(EXIT_FAILURE);
			}

			if (fclose(stdin)) {
				fatal();
				exit(EXIT_FAILURE);
			}
		}
		else { //father
			waitpid (pid, &status, 0);
		}
	}
	return(EXIT_SUCCESS);
}

/******************************************************************************************/

/*
*	EN:
*	this function executes the command with the pipe symbol
*
*	PT:
 *	Esta função executa o comando com o modificador pipe. A saída do primeiro comando
 *	é a entrada do segundo.
 *
 */

int execute_command_pipe(command_line_t command, command_line_t *com_aux) {
	pid_t pid;
	int status;

	//fork the child process to execute the command
	pid = fork ();

	if (pid == -1 ) { //pid < 0 error
		fatal();
	}
	else {
		if (pid == 0) { //pid == 0 child
			execute_pipe(command, com_aux);  
			if (execvp(command.argv[0],command.argv) == -1) {
				fprintf(stderr,"%s: command not found.\n", command.argv[0]);
				exit(EXIT_FAILURE);
			}
		}
		else { //father
			waitpid (pid, &status, 0);
		}
	}
	return(EXIT_SUCCESS);
}

/******************************************************************************************/

/*
*	conection beteween two pipe commands
 *
 */

int execute_pipe(command_line_t command, command_line_t *com_aux) {
	pid_t pid2;
	int mypipe[2];
  	
	pipe(mypipe);
			
	if ((pid2 = fork()) < 0) { //pid < 0 error
		fatal();
		exit(EXIT_FAILURE);
	}

	if (pid2 == 0) { //pid == 0 child
		fclose(stdout);
		close(1);
		//duplicate file's descritor to stdout
		dup(mypipe[1]);
		close(mypipe[0]);

		if (execvp(command.argv[0], command.argv) == -1) {
			fprintf(stderr,"%s: command not found.\n", command.argv[0]);
			exit(EXIT_FAILURE);
		}
		exit(EXIT_SUCCESS);
 	}
 	else { //father
		fclose(stdin);
		close(0);
 		//duplicate file's descritor to stdin
		dup(mypipe[0]);
		close(mypipe[1]);      			    

		select_command(*com_aux);
 		waitpid(pid2, NULL, WUNTRACED);
		exit(EXIT_SUCCESS);
	}
	return(EXIT_SUCCESS);
}

/******************************************************************************************/

/*
 *	remove the "argv's" from the commands struct begning at p position.
 *
 */

int free_com(command_line_t *command, int p) {
	int i = 0;
	for (i = p; i < command->argc; i++)	{
		command->argv[i] = NULL;
		free(command->argv[i]);
	}
	command->argc = p;
	return(EXIT_SUCCESS);
}
